import { toRefs } from "vue";

const isObject = o => o !== null && typeof o === 'object';
const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (target, key) => hasOwn.call(target, key);
const convert = target => isObject(target) ? reactive(target) : target;
export function reactive(target) {
  if (!isObject(target)) return target;

  const handler = {
    get(target, key, receiver) {
      // 收集依赖
      // console.log('get:', target, key);
      track(target, key);
      const result = Reflect.get(target, key, receiver);
      return convert(result);
    },
    set(target, key, value, receiver) {
      const oldValue = Reflect.get(target, key, receiver);
      let result = true;

      if (oldValue !== value) {
        result = Reflect.set(target, key, value, receiver);
        // 派发更新
        // console.log('set:', target, key, value);
        trigger(target, key);
      }

      return result;
    },
    deleteProperty(target, key) {
      const hasKey = hasOwn(target, key);
      const result = Reflect.deleteProperty(target, key);

      if (hasKey && result) {
        // 派发更新
        // console.log('delete:', target, key);
        trigger(target, key);
      }
      return result;
    }
  }

  return new Proxy(target, handler);
}

let activeEffect = null;
export function effect(callback) {
  activeEffect = callback;
  // 访问响应式对象睡醒，去收集依赖
  callback();
  activeEffect = null;
}

const targetMap = new WeakMap();
export function track(target, key) {
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }

  dep.add(activeEffect);
}

export function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const dep = depsMap.get(key);

  if (dep) {
    dep.forEach(effect => {
      effect();
    })
  }
}

export function ref(raw) {
  if (isObject(raw) && raw.__v_isRef) return;

  let value = convert(raw);
  const r = {
    __v_isRef: true,
    get value() {
      track(r, 'value');
      return value;
    },
    set value(newValue) {
      if (newValue !== value) {
        raw = newValue;
        value = convert(raw);
        trigger(r, 'value');
      }
    }
  }

  return r;
}

export function toRefs(proxy) {
  const ret = proxy instanceof Array ? new Array(proxy.length) : {};

  for (const key in proxy) {
    ret[key] = toProxyRef(proxy, key);
  }

  return ret;
}

function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      return proxy[key];
    },
    set value(newValue) {
      proxy[key] = newValue;
    }
  }
  return r;
}

export function computed(getter) {
  const result = ref();

  effect(() => (result.value = getter));

  return result;
}